﻿using System.Collections.Generic;
using AntBot.Algorithms;

namespace AntBot
{
	public class Route : IRoute
	{
		public Location FromLoc { get; set; }
		public Location ToLoc { get; set; }

		//if IspathToFood returns adjacent square inside SpawnRadius
		public Location LastLoc
		{
			get
			{
				if (!IsPathToFood || Path == null) return ToLoc;
				return new Location(Path[Path.Count - 1].X, Path[Path.Count - 1].Y);
			}
		}

		//Path to food leads not exactly to ToLoc, but to adjacent square inside SpawnRadius
		public bool IsPathToFood { get; set; }

		public int MapHash;
		public bool IsShortest;
		public bool NoRoute;

		public List<Location> Path;

		protected Route() {} //for NoRoute

		public static Route NewNoRoute()
		{
			return new NoRoute();
		}

		public static Route NewNoRoute(Location fromLoc, Location toLoc)
		{
			AntBot.NoRoute.Instance.FromLoc = fromLoc;
			AntBot.NoRoute.Instance.ToLoc = toLoc;
			return AntBot.NoRoute.Instance;
		}

		public Route(List<PathFinderNode> path, int mapHash, bool isShortest)
			: this(Router.PathFinderNodeListToLocationList(path), mapHash, isShortest)
		{}

		public Route(List<Location> path, Location fromLoc, Location toLoc, int mapHash, bool isShortest)
		{
			NoRoute = (path == null);
			MapHash = mapHash;

			if (path == null) return;

			Path = path;

			FromLoc = fromLoc;
			ToLoc = toLoc;

			IsShortest = isShortest;
		}

		public Route(List<Location> path, int mapHash, bool isShortest)
			: this(
				path,
				new Location(path == null ? 0 : path[0].X, path == null ? 0 : path[0].Y),
				new Location(path == null ? 0 : path[path.Count - 1].X, path == null ? 0 : path[path.Count - 1].Y),
				mapHash,
			isShortest)
		{}

		public State.Directions GetFirstStepDirection()
		{
			if (Path == null || Path.Count <= 1)
				return State.Directions.Unknown;

			Location fromLoc = Path[0];
			Location toLoc = Path[1];

			if (fromLoc == toLoc)
				return State.Directions.Unknown;

			if (toLoc.X == fromLoc.X && toLoc.Y == fromLoc.Y - 1) return State.Directions.North;
			if (toLoc.X == fromLoc.X + 1 && toLoc.Y == fromLoc.Y) return State.Directions.East;
			if (toLoc.X == fromLoc.X && toLoc.Y == fromLoc.Y + 1) return State.Directions.South;
			if (toLoc.X == fromLoc.X - 1 && toLoc.Y == fromLoc.Y) return State.Directions.West;

			//edge was wrapped
			if (toLoc.X == fromLoc.X && fromLoc.Y == 0) return State.Directions.North;
			if (toLoc.X == 0 && toLoc.Y == fromLoc.Y) return State.Directions.East;
			if (toLoc.X == fromLoc.X && toLoc.Y == 0) return State.Directions.South;
			if (fromLoc.X == 0 && toLoc.Y == fromLoc.Y) return State.Directions.West;

			return State.Directions.Unknown;
		}

		public bool IsOutdated(int mapHash)
		{
			return MapHash != mapHash;
		}

		public static bool operator ==(Route r1, Route r2)
		{
			if (ReferenceEquals(r1, r2))
			{
				// handles if both are null as well as object identity
				return true;
			}

			if ((object)r1 == null || (object)r2 == null)
			{
				return false;
			}

			if (r1.GetType() == AntBot.NoRoute.Instance.GetType() ||
				r2.GetType() == AntBot.NoRoute.Instance.GetType())
				return false;

			return ((r1.FromLoc == r2.FromLoc) && (r1.ToLoc == r2.ToLoc)) ||
				((r1.FromLoc == r2.ToLoc) && (r1.ToLoc == r2.FromLoc));
		}

		public static bool operator !=(Route r1, Route r2)
		{
			return !(r1 == r2);
		}

		public override bool Equals(object obj)
		{
			if (obj == null /*|| GetType() != obj.GetType()*/)
				return false;

			return this == (Route)obj;
		}

		public override int GetHashCode()
		{
			return FromLoc.GetHashCode() + ToLoc.GetHashCode();
		}

		public override string ToString()
		{
			if (Path == null)
				return "No path from " + FromLoc + " to " + ToLoc;

			return FromLoc + "->" + ToLoc;
		}

		public string ToStringDetail()
		{
			if (Path == null)
				return "No path from " + FromLoc + " to " + ToLoc;

			string result = "";
			foreach (Location location in Path)
			{
				if (result != "") result += ",";
				result += "(" + location.X + "," + location.Y + ")";
			}
			return IsShortest ? "shortest " + result : result;
		}
	}
}